﻿package ygg.encoding.swf
{
	import flash.geom.Rectangle;
	import flash.utils.ByteArray;
	import flash.utils.describeType;
	import flash.utils.Endian;
	import flash.utils.*;
	
	import j.sus2maj7.encoding.*;
	import j.sus2maj7.context.*;
	
	import ygg.encoding.swf.*;
	import ygg.encoding.swf.version.*;
	import ygg.encoding.swf.structure.*;
	import ygg.encoding.swf.abc.*;
	import ygg.encoding.swf.tag.*;
	import j.sus2maj7.encoding.*;
	
	import j.sus2maj7.util.ByteUtil;
	
	public class SwfCodec extends AbstractCodec
	{
		
		override public function decode(binary:IBinary, context:IContext=null):*
		{
			
			binary.position = 0;
			binary.endian = Endian.LITTLE_ENDIAN;
			
			var o:Swf = new Swf();
			o.header = decodeHeader( SwfBinary(binary) );
			
			var tf:Object = decodeBody( SwfBinary(binary) );
			o.tags = tf.tags;
			o.frames = tf.frames;
			trace('File Attributes');
			if (tf.fileAttr)
			{
				
				//TagEncoding(tf.fileAttr).decode();
				//o.fileAttr = tf.fileAttr.object;
			}
			
			return o;
			
		}
		
		// head is same for both amf0 and amf3
		protected function decodeHeader(b:SwfBinary):SwfHeader
		{
			trace('decodeHead');
			
			//signature signature signature version
			
			var signature:String = b.readUTFBytes(3);
			var version:uint = b.readUnsignedByte();
			
			var fileLength:uint = b.readUnsignedInt();
			
			trace(signature, version.toString(16), version.toString(2), fileLength, b.length);
			
			if (signature == 'CWS' && version >= 6) //compressed SWF6+
			{
				b.readBytes(b.ba);
				trace(b.length);
				b.uncompress();
				
				trace("decompressed ", fileLength, b.length, b.position);
				
				ByteUtil.print(b.ba);
			}
			else if (signature == 'FWS' && version >= 4)
			{
				//not compressed
				
			}
			else
			{
				throw new Error("This version is unsupported. " + version);
			}
			
			var frameSize:Rect = b.decodeRect();
			/*
			var frame:Array = readBox(byteArray);
			
			propsVO.width = Math.round((frame[1]-frame[0])/20);
			propsVO.height = Math.round((frame[3]-frame[2])/20);

			var fps_f:uint = byteArray.readUnsignedByte();
			var fps_i:uint = byteArray.readUnsignedByte();
			//*/
			
			//var frameRate:uint = (b.readUnsignedByte() << 8 | b.readUnsignedByte()); //(fps_i+fps_f/256);
			
			var fixedB:uint = b.readUnsignedByte();
			var fixedA:uint = b.readUnsignedByte();
			
			trace(fixedB, fixedA);
			var frameRate:Number =  fixedA + (fixedB / 256); //(fps_i+fps_f/256);
			
			var frameCount:uint = b.readUnsignedShort();
			
			
			return new SwfHeader(version, fileLength, new Rectangle(0,0, frameSize.xMax * .05, frameSize.yMax * .05), frameRate, frameCount);
			
			
		}
		protected function decodeBody(b:SwfBinary):Object
		{
			trace('decodeBody');
			
			var tagsAndFrames:Object = decodeTags(b);
			
			return tagsAndFrames;
		}
		
		private function decodeTags(b:SwfBinary):Object
		{
	
			var TagTypeAndLength:uint;
			var type:int;
			var length:int;
			
			
			var tagsAndFrames:Object = { };
			var tags:Vector.<TagEncoding> = new Vector.<TagEncoding>;
			var frames:Array = [];
			var fileAttr:TagEncoding;
			
			//var tagEncodingFactory:IEncodingFactory = new TagEncodingFactory();
			var tagCodecFactory:ICodecFactory = new TagCodecFactory();
			
	        while (b.bytesAvailable)
	        {
				TagTypeAndLength = b.readUnsignedShort();
				type = TagTypeAndLength >> 6;
				length = TagTypeAndLength & 0x3F;
				
				if (length == 0x3F)
				{
					length = b.readUnsignedInt();
				}
				trace(TagType.getName(type) + " " + length + "b " + (100 * length / b.length).toFixed(2) + "%");
				
				var tagBytes:ByteArray = new ByteArray();
				if (length > 0)
				{
					b.readBytes(tagBytes, 0, length);
				}
				tagBytes.endian = Endian.LITTLE_ENDIAN;
				
				var tagEncoding:TagEncoding = new TagEncoding();
				tagEncoding.type = type;
				tagEncoding.codec = tagCodecFactory.getDecoder(type);
				
				tagEncoding.binary = new TagBinary(tagBytes);
				
				tags[tags.length] = tagEncoding;
				
				
				if (type == TagType.FILE_ATTRIBUTES)
				{
					fileAttr = tagEncoding;
				}
				else if (type == TagType.FRAME_LABEL)
				{
					frames[frames.length] = [];
				}
				else
				{
					if (frames.length)
						frames[frames.length - 1][frames[frames.length - 1].length] = tagEncoding;
					
				}
				
				/*
				switch (tagObj.type)
				{
					case 0: break;
					case Tag.DO_ABC_2:
						var pos1:int = tagObj.binary.position;
						tagObj.binary.readInt()
						trace("\nabc name " + SwfBinary(tagObj.binary).readString() + "\n");
						//length -= (tagObj.binary.position - pos1);
						
						// fall through
					case Tag.DO_ABC:
						//var b2:ByteArray = new ByteArray();
						//b2.endian = Endian.LITTLE_ENDIAN;
						
						//b.readBytes(b2,0,length)
						//new Abc(b2,sb).dump("  ")
						//new Abc(b2).dump("  ")
						trace("\n")
						break;
					default:
						//b.position += length
				}
				//*/
	        }
			
			tagsAndFrames.tags = tags;
			tagsAndFrames.frames = frames;
			tagsAndFrames.fileAttr = fileAttr;
			
			return tagsAndFrames;
		}

///////////////////////////
/*		////function to find start position of loaded in swf ?
		static public function shiftBytesToStart(bytes:ByteArray):ByteArray
		{
			
			var shiftValue:int = -1;
			for (var i:uint = 0; i < 50; i++) {
				if (String.fromCharCode(bytes[i + 1]) == "W" &&
				    String.fromCharCode(bytes[i + 2]) == "S") {
					if (String.fromCharCode(bytes[i]) == "C" ||
					    String.fromCharCode(bytes[i]) == "F" ) {
					    	shiftValue = i;
					    	break;
					    }
				}
			}
			if (shiftValue > -1) {
				trace ("shifting by " + shiftValue);
				var newBytes:ByteArray = new ByteArray();
				for (var j:Number = 0; j < bytes.length - shiftValue; j++) {
					newBytes[j] = bytes[j + shiftValue];
				}
				
				trace ("length:" + newBytes.length);
				trace (bytes.length);
				return newBytes;
			}
			throw new SWFParseError("invalid swf format");
			return null;
		}

		//why not use decodeRect?
		static private function readBox(b:ByteArray):Array
		{
			var c:Array = [];
			var current:uint = b.readUnsignedByte();
			var size:uint = current>>3;
			var off:int = 3;
			for (var i:int = 0; i < 4; i += 1)
			{
				c[i] = current << (32 - off) >> (32 - size);
				off -= size;
				while (off < 0)
				{
					current = b.readUnsignedByte();
					c[i] |= off < -8?current << ( -off - 8):current >> ( -off - 8);
					off += 8;
				}
			}
			return c;
		}

 * var tags:ArrayCollection = TagParser.getTags(byteArray);
			
			
			
			var loadedAS3Classes:ArrayCollection = new ArrayCollection();
			
			for (var k:uint = 0; k < tags.length; k++)
			{
				var tag:Tag = Tag(tags.getItemAt(k));
				//
				
				if (tag is DefineBinaryDataTag)
				{
					trace(tag.toString() );
					//trace(tag.tagData );
					var file:File = File.desktopDirectory.resolvePath('binary_' + k);
					var wr:File = new File(file.nativePath);
					// Create a stream and open the file for asynchronous reading
					var stream:FileStream = new FileStream();
					stream.open( wr, FileMode.WRITE );
					stream.writeBytes(tag.byteData, 6);
					stream.close();

				}
				
				if (tag is DoABCTag)
				{
					//trace(tag.toString());
					//trace(tag.tagData);
					var abcTag:DoABCTag = DoABCTag(tag);
					abcTag.tagData; //parses here, why?
					var classes:ArrayCollection = abcTag.classes;
					for (var j:uint = 0; j < classes.length; j++)
					{
						var cls:AS3Class = classes[j];
						if (cls.packageName.indexOf('com.gaiaframework.panel.auth') == 0)
						{
							
							//trace(cls.packageName + '.' + cls.className);
							//trace(cls.traits.methods );
						}
						loadedAS3Classes.addItem(cls);
					}
				}
			}
			
			//trace(loadedAS3Classes);
			//*/
			
		override public function encode(o:*, context:IContext=null):IBinary
		{
			trace('encode', o);
			
			
			o = Swf(o);
			
			var b:SwfBinary = new SwfBinary();
			
			b.endian = Endian.BIG_ENDIAN;
			
			var body:IBinary = encodeBody(o.body);
			
			encodeHeader(o.header, body.length);
			
			b.writeBytes(body.ba);
			
			return b;
			
		}
		
		public function encodeBody(bodyObject:Object):IBinary
		{
			trace('encodeBody');
			
			var body:IBinary = new SwfBinary();
			
			
			return body;
		}
		
		// head is same for both amf0 and amf3
		protected function encodeHeader(header:SwfHeader, bodyLen:int):void
		{
			trace('encodeHeader');
			
			
			
		}
	}
}